Imports System.Xml
Public Class FxXMLReader
    Public Shared Function ParseFxXML(ByRef aFileName As String) As FxFormatStructures
        Dim iXD As New XmlDocument()
        Dim iXNode As XmlNode
        Dim iXNode1 As XmlNode
        Dim iXNode2 As XmlNode

        Dim iFxFS As New FxFormatStructures
        Dim iEnum As FxEnum
        Dim iStructure As FxStructure

        Dim iName As String
        Dim iConstant As Double
        Dim iValue As String

        iXD.Load(aFileName)

        ' Enums
        For Each iXNode In iXD.SelectNodes("/root/enums/enum")
            iName = iXNode.ChildNodes(0).InnerText
            iEnum = New FxEnum(iName)
            For Each iXNode1 In iXNode.SelectNodes("values/value")
                iValue = ""
                iConstant = 0
                For Each iXNode2 In iXNode1.ChildNodes
                    Select Case iXNode2.LocalName
                        Case "name"
                            iName = iXNode2.InnerText
                        Case "constant"
                            iConstant = [Convert].ToDouble(iXNode2.InnerText)
                        Case Else
                            Throw New Exception("Unknown xml node /root/enums/enum/values/value : " + iXNode2.LocalName)
                    End Select
                Next
                iEnum.AddItem(iConstant, iName)
            Next
            iFxFS.AddEnum(iEnum)
        Next

        For Each iXNode In iXD.SelectNodes("/root/structures/structure")
            iName = iXNode.ChildNodes(0).InnerText
            iStructure = New FxStructure(iName) 
            For Each iXNode1 In iXNode.SelectNodes("attributes/attribute")
                iName = ""
                Dim iSize As Integer = -1
                Dim iSizeStr As String = ""
                Dim iEnumName As String = ""
                Dim iFlagName As String = ""
                Dim iTypeStr As String = ""
                Dim iFxStrAttr As FxStructureAttribute
                Dim iAttrType As FxStructureAttributeType
                For Each iXNode2 In iXNode1.ChildNodes
                    Select Case iXNode2.LocalName
                        Case "name"
                            iName = iXNode2.InnerText
                        Case "size"
                            iSizeStr = iXNode2.InnerText
                        Case "type"
                            iTypeStr = iXNode2.InnerText
                        Case "enum"
                            iEnumName = iXNode2.InnerText
                        Case "flag"
                            iFlagName = iXNode2.InnerText
                        Case Else
                            Throw New Exception("Unknown attribute in /root/structures/structure/attributes/attribute : " + iXNode2.LocalName)
                    End Select
                Next
                If IsNumeric(iSizeStr) Then
                    iSize = [Convert].ToInt64(iSizeStr)
                End If
                If iTypeStr = "" Then
                    Select Case iSize
                        Case 1
                            iAttrType = FxStructureAttributeType.FxByte
                        Case 2
                            iAttrType = FxStructureAttributeType.FxWord
                        Case 4
                            iAttrType = FxStructureAttributeType.FxDWord
                        Case 8
                            iAttrType = FxStructureAttributeType.FxQWord
                        Case Else
                            Throw New Exception("Unable to resolve attribute type of : " + iName)
                    End Select
                Else
                    Try
                        iAttrType = FxStructureAttribute.GetAttributeType(iTypeStr)
                    Catch ex As Exception
                        iAttrType = FxStructureAttributeType.FxString
                    End Try

                End If
                iFxStrAttr = New FxStructureAttribute(iName, iSize, iAttrType, iSizeStr, iEnumName, iFlagName)
                iStructure.AppendAttribute(iFxStrAttr)
            Next
            iFxFS.AddStructure(iStructure)
        Next
        Return iFxFS
    End Function
    Public Shared Function ParseFileData(ByRef aFxFS As FxFormatStructures, ByVal aSoureceFileName As String, ByVal aXmlFileName As String) As FxData
        Dim iXD As New XmlDocument()
        Dim iXNode As XmlNode
        Dim iXNode1 As XmlNode
        Dim iXNode2 As XmlNode

        Dim iFxFS As New FxFormatStructures
        Dim iEnum As FxEnum
        Dim iStructure As FxStructure

        Dim iName As String
        Dim iConstant As Double
        Dim iCountStr As String
        Dim iCount As Integer = -1
        Dim iMarkerBytes() As Byte
        Dim iMarkerLen As Integer
        Dim iReadBytes() As Byte
        Dim iMarkerStr As String
        Dim iReadStr As String
        Dim iFxS As FxStream
        Dim iCurrentPosition As Long
        Dim iLastPostion As Long
        Dim iEncoding As New System.Text.ASCIIEncoding
        Dim iFxData_Root As New FxData(0, -1, "File", FxStructureAttributeType.FxByte, Nothing, Nothing)
        iFxS = New FxStream(aSoureceFileName)

        iXD.Load(aXmlFileName)

        For Each iXNode In iXD.SelectNodes("/root/format/data")
            Dim iFxData_Data As FxData
            iCurrentPosition = iFxS.GetOffset()

            iName = iXNode.Attributes.GetNamedItem("name").InnerText
            iCountStr = iXNode.Attributes.GetNamedItem("count").InnerText
            If IsNumeric(iCountStr) Then
                iCount = [Convert].ToDouble(iCountStr)
            End If
            iMarkerStr = iEncoding.GetString(FxStream.ConvertStringToByteArray(iXNode.Attributes.GetNamedItem("marker").InnerText))
            'iMarkerBytes = FxStream.ConvertStringToByteArray(iMarkerStr)

            iFxData_Data = New FxData(iCurrentPosition, -1, iName, FxStructureAttributeType.FxHexBuffer, Nothing, Nothing)
            iReadStr = iEncoding.GetString(iFxS.ReadBytesWithoutForward(iMarkerStr.Length))
            If iCountStr = "multiple" Then
                While iReadStr = iMarkerStr
                    For Each iXNode1 In iXNode.SelectNodes("structurename")
                        Dim iFxData_Struct As FxData
                        Dim iStructureName As String
                        Dim iFxStruct As FxStructure
                        Dim iDisplayNameSource As String = ""
                        Dim iDisplayName As String
                        If iXNode1.Attributes.Count > 0 Then
                            iDisplayNameSource = iXNode1.Attributes.GetNamedItem("displayname").InnerText
                        End If
                        iStructureName = iXNode1.InnerText
                        iFxStruct = aFxFS.GetStructure(iStructureName)
                        iFxData_Struct = ReadStructure(iFxFS, iFxS, iFxStruct)
                        If iDisplayNameSource <> "" Then
                            iDisplayName = GetAttributeValue(iFxData_Struct, iDisplayNameSource)
                            iFxData_Struct.Header = iDisplayName
                        End If

                        iFxData_Data.AppendChild(iFxData_Struct)
                    Next
                    iReadStr = iEncoding.GetString(iFxS.ReadBytesWithoutForward(iMarkerStr.Length))
                End While
            Else
                If iReadStr = iMarkerStr Then
                    For Each iXNode1 In iXNode.SelectNodes("structurename")
                        Dim iFxData_Struct As FxData
                        Dim iStructureName As String
                        Dim iFxStruct As FxStructure
                        Dim iDisplayNameSource As String = ""
                        Dim iDisplayName As String
                        If iXNode1.Attributes.Count > 0 Then
                            iDisplayNameSource = iXNode1.Attributes.GetNamedItem("displayname").InnerText
                        End If

                        iStructureName = iXNode1.InnerText
                        iFxStruct = aFxFS.GetStructure(iStructureName)
                        iFxData_Struct = ReadStructure(iFxFS, iFxS, iFxStruct)
                        If iDisplayNameSource <> "" Then
                            iDisplayName = GetAttributeValue(iFxData_Struct, iDisplayNameSource)
                            iFxData_Struct.Header = iDisplayName
                        End If

                        iFxData_Data.AppendChild(iFxData_Struct)
                    Next
                End If
            End If
            iFxData_Data.UpdateSize()
            If iFxData_Data.Size > 0 Then
                iFxData_Root.AppendChild(iFxData_Data)
            End If
        Next
        Return iFxData_Root
    End Function

    Private Shared Function ReadStructure(ByRef aFxFS As FxFormatStructures, ByRef aFxS As FxStream, ByRef aFxStruct As FxStructure) As FxData
        Dim iAttr As FxStructureAttribute
        Dim iCurrentOffset As Long
        Dim iFxData As FxData
        Dim iEncoding As New System.Text.ASCIIEncoding
        iCurrentOffset = aFxS.GetOffset()
        Dim iCurFxData As New FxData(iCurrentOffset, -1, aFxStruct.Name, FxStructureAttributeType.FxHexBuffer, Nothing, Nothing)
        For Each iAttr In aFxStruct.Attributes
            iCurrentOffset = aFxS.GetOffset()
            Select Case iAttr.Type
                Case FxStructureAttributeType.FxFlag
                    Dim iData As Int64
                    Select Case iAttr.Size
                        Case 1
                            iData = aFxS.ReadByte
                        Case 2
                            iData = aFxS.ReadWord
                        Case 4
                            iData = aFxS.ReadDWord
                        Case 8
                            iData = aFxS.ReadQWord
                        Case Else
                            Throw New Exception("Unknonw Flag value " + iAttr.Name)
                    End Select
                    iFxData = New FxData(iCurrentOffset, iAttr.Size, iAttr.Name, iAttr.Type, iData, iAttr.Flag)
                Case FxStructureAttributeType.FxEnum
                    Dim iData As Int64
                    Select Case iAttr.Size
                        Case 1
                            iData = aFxS.ReadByte
                        Case 2
                            iData = aFxS.ReadWord
                        Case 4
                            iData = aFxS.ReadDWord
                        Case 8
                            iData = aFxS.ReadQWord
                        Case Else
                            Throw New Exception("Unknonw Enum value " + iAttr.Name)
                    End Select
                    iFxData = New FxData(iCurrentOffset, iAttr.Size, iAttr.Name, iAttr.Type, iData, iAttr.FEnum)
                Case FxStructureAttributeType.FxByte
                    Dim iData As Byte
                    iData = aFxS.ReadByte()
                    iFxData = New FxData(iCurrentOffset, 1, iAttr.Name, iAttr.Type, iData, Nothing)
                Case FxStructureAttributeType.FxWord
                    Dim iData As UInt16
                    iData = aFxS.ReadWord()
                    iFxData = New FxData(iCurrentOffset, 2, iAttr.Name, iAttr.Type, iData, Nothing)
                Case FxStructureAttributeType.FxDWord
                    Dim iData As UInt32
                    iData = aFxS.ReadDWord()
                    iFxData = New FxData(iCurrentOffset, 4, iAttr.Name, iAttr.Type, iData, Nothing)
                Case FxStructureAttributeType.FxQWord
                    Dim iData As UInt64
                    iData = aFxS.ReadQWord()
                    iFxData = New FxData(iCurrentOffset, 8, iAttr.Name, iAttr.Type, iData, Nothing)
                Case FxStructureAttributeType.FxString
                    Dim iSize As Integer
                    Dim iStr As String
                    If iAttr.Size = -1 Then
                        iSize = [Convert].ToInt32(GetAttributeValue(iCurFxData, iAttr.SizeSource))
                    Else
                        iSize = iAttr.Size
                    End If
                    iStr = iEncoding.GetString(aFxS.ReadBytes(iSize))
                    iFxData = New FxData(iCurrentOffset, iSize, iAttr.Name, iAttr.Type, iStr, Nothing)
                Case FxStructureAttributeType.FxScanTillNullChar
                    Throw New Exception("Unknown Type of attribute : " + iAttr.Name)
                Case FxStructureAttributeType.FxHexBuffer
                    Dim iSize As Integer
                    If iAttr.Size = -1 Then
                        iSize = [Convert].ToInt32(GetAttributeValue(iCurFxData, iAttr.SizeSource))
                    Else
                        iSize = iAttr.Size
                    End If
                    aFxS.SkipBytes(iSize)
                    iFxData = New FxData(iCurrentOffset, iSize, iAttr.Name, iAttr.Type, Nothing, Nothing)
                Case Else
                    Throw New Exception("Unknown Type of attribute : " + iAttr.Name)
            End Select
            iCurFxData.AppendChild(iFxData)
        Next
        iCurFxData.UpdateSize()
        Return iCurFxData
    End Function
    Public Shared Function GetAttributeValue(ByRef aFxData As FxData, ByRef aStr As String) As Object
        If aStr.StartsWith("this.") Then
            aStr = aStr.Substring(5)
        End If
        Dim iFxData As FxData
        For Each iFxData In aFxData.Childs
            If iFxData.Name = aStr Then
                Return iFxData.Data
            End If
        Next
        Throw New Exception("Value not found : " + aStr)
        Return Nothing
    End Function

End Class
